use std::hint::spin_loop;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time::Duration;

const MUTEX_TIMEOUT: i32 = 10;

#[derive(Debug)]
pub struct Umutex {
    l: AtomicBool,
}

impl Default for Umutex {
    fn default() -> Self {
        Self {
            l: AtomicBool::new(false),
        }
    }
}

impl Umutex {
    fn try_lock(&mut self) -> bool {
        !self.l.swap(true, Ordering::Acquire)
    }
    pub fn lock(&mut self) -> bool {
        let mut c = 0_i32;
        while !self.try_lock() {
            spin_loop();
            c += 1;
            if c > MUTEX_TIMEOUT*1000 {
                return false;
            }
        }
        return true;
    }
    pub fn unlock(&mut self) {
        self.clear();
    }
    fn clear(&mut self) {
        self.l.swap(false, Ordering::Release);
    }
    fn reset(&mut self) {
        self.clear();
    }
    pub fn value(&self) -> bool {
        self.l.load(Ordering::Relaxed)
    }
    pub fn check(&mut self) -> bool {
        let r = self.value();
        if !r {
            self.clear()
        }
        r
    }
    pub fn check_lock(&mut self, mn: String) -> bool {
		let mut c : i32 = MUTEX_TIMEOUT;
		while !self.try_lock() {
			thread::sleep(Duration::from_millis(10));
			c -= 1;
			if c==0 {break;}
		}
		if c!=0 {
			self.unlock();
			true
		} else {
			self.reset();
			println!("fail to get lock: {}", mn);
			false
		}
	}
}

#[derive(Debug)]
pub struct ScopedLock<'a> {
    mutex: &'a mut Umutex,
}

impl<'a> ScopedLock<'a> {
    pub fn new(m: &'a mut Umutex) -> Self {
        m.lock();
        Self { mutex: m }
    }
}

impl<'a> Drop for ScopedLock<'a> {
    fn drop(&mut self) {
        self.mutex.unlock()
    }
}

